package model.parser;

import java.util.*;

import model.semantics.Node;

import util.resources.ResourceManager;

public class FunctionTable<T> {

    private Map<String, FunctionDefinition<T>> definitionMap;

    public FunctionTable() {
        definitionMap = new HashMap<String, FunctionDefinition<T>>();
    }

    public FunctionDefinition<T> getDefinition(String symbol) {
        if (!definitionMap.containsKey(symbol)) {
            definitionMap.put(symbol, new UserFunctionDefinition<T>());
        }
        return definitionMap.get(symbol);
    }

    /**
     * Add a user-defined function to the function table.
     * 
     * @param symbol
     *            the symbol representing the function (the name.)
     * @param function
     *            the node containing the function body.
     * @param args
     *            variable parameters to the function.
     */
    public void addUserDefinedFunction(String symbol, Node<T> function,
            List<String> args) {
        UserFunctionDefinition<T> definition = (UserFunctionDefinition<T>) getDefinition(symbol);
        definition.myBody = function;
        definition.argLabels = args;
    }

    public void setFunctionBody(String symbol, Node<T> function) {
        UserFunctionDefinition<T> definition = (UserFunctionDefinition<T>) definitionMap
                .get(symbol);
        definition.setBody(function);
    }

    /**
     * Load a syntax file and add function definitions to the table.
     * 
     * @param syntaxFile
     *            the syntax file.
     */
    public void addFunctionsFromSyntaxFile(String syntaxFile) {
        ResourceManager resources = ResourceManager.getManager(syntaxFile);

        // Pull out every function from the resource file and define it.
        for (String symbol : resources) {
            String[] params = resources.getStringArray(symbol);

            // Throw an exception if we were unable to match this symbol to a
            // token type.
            if (params == null) {
                throw ParserException.BAD_TOKEN;
            }

            BuiltinFunctionDefinition<T> definition = new BuiltinFunctionDefinition<T>();
            definition.label = symbol;
            definition.order = Integer.parseInt(params[0]);
            definition.precedence = Integer.parseInt(params[1]);
            definition.className = params[2];
            definition.callArgs = Arrays.copyOfRange(params, 3, params.length);

            definitionMap.put(symbol, definition);
        }
    }
}
